import threading
import sched
import time, datetime
import getopt, sys, os, getpass
import subprocess
import logging
import eventClient
import sysutils
import testbedReservation


#Purpose: compare the events in gCal with reservations running on the intended machines.
class ReservationChecker:

	def __init__(self, username, password, url, logfile, email):
		self.user = username
		self.pswd = password
		self.url = url
		#log file name; default is verifier.log
		self.logfile = logfile
		#list of email addresses for sending error messages
		self.email = email
		#logging utility
		self.reslog = logging.basicConfig(filename='verifier.log', level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M ')
		#Keep track of Reservation objs scheduled for verification
		self.gCalEvents = []
		#Keep handles on scheduler events in case a verification task needs to be cancelled.
		self.runningSchedules = []
		#run separate thread to perform check at scheduled time
		self.schedule = sched.scheduler(time.time, time.sleep)

	#This fxn is called by the scheduler to check if the host and image for 
	#a machine is setup at the expected time, based on gCal.
	def machine_check(self, host):
		chkmsg = "*** running check on host "  + host + " ***"
		print chkmsg
		logging.info(chkmsg)
		resMatch = [ev for ev in self.gCalEvents if ev.host == host][0]
		gCalImage = resMatch.image
		(sysImage, err) = sysutils.chk_image(host)
		resmsg = ""
		if sysImage:
			sysImage = sysImage.lstrip().rstrip()
			if sysImage == gCalImage:
				resmsg = "Configuration verified: host=" + host + " image=" + sysImage
				print resmsg
				logging.info(resmsg)
			else:
				resmsg = "Error for host " + host + " : sys image = " + sysImage + ";  gCal image = " + gCalImage
				resmsg = resmsg + "\nReservation start: " + resMatch.startDate + "\nReservation end: " + resMatch.endDate
				if resMatch.name:
					resmsg = resmsg + "\nOwner name: " + resMatch.name
				print resmsg
				logging.warning(resmsg)
				if self.email:
					for addr in self.email:
						(out, err) = sysutils.send_email("AUTO-VERIFIER: Wrong host image", resmsg, addr)
						if err:
							print "Email notification had an error."
							print err
							logging.error("Email notification error")
							logging.error(err)
		else:
			resmsg = "No info returned from machine configuration check"
			print resmsg
			logging.error(resmsg)
			if err:
				print err
				logging.error(err)

	def reset(self):
		for ev in self.runningSchedules:
			try:
				self.schedule.cancel(ev)
			except ValueError:
				pass

	def get_gCal_caldav(self):
		#retrieve info from gCal for entire day
		startRange = datetime.datetime.strptime( eventClient.today_utc(), "%Y%m%dT%H%M%SZ")
		endRange = datetime.datetime.strptime( eventClient.today_end_utc(), "%Y%m%dT%H%M%SZ")
		#Object to make requests from gCal.
		caldav = eventClient.CaldavConnection(self.user, self.pswd, self.url)
		requestEv = caldav.getReportRange(startRange, endRange)
		#Look at brief info about events
		print ""
		print "*** gCal Event summaries: ***"
		logging.info("*** gCal Event summaries: ***")
		for ev in requestEv:
			print ev
			logging.info(str(ev) + "\n-------------------")
			print "----------------"

		requestRes = []
		for ev in requestEv:
			#Fill in reservations per host
			(res, errdata) = testbedReservation.gCalReservation(ev)
			if res:
				requestRes.extend(res)
			elif errdata:
				subject = "AUTO-VERIFIER: " + " ".join(errdata.split()[:2])
				if self.email:
					for addr in self.email:
						(out, err) = sysutils.send_email(subject, errdata, addr)
						if err:
							print "Email notification had an error."
							print err
							logging.error("Email notification error")
							logging.error(err)
		return requestRes

	#Update the scheduler with events in gCal that need to be verified
	#in case there were any new events or modifications
	def update(self):
		self.reset()
		#get list of Reservation objects
		requestRes = self.get_gCal_caldav()
		if not requestRes:
			return
		#check if reservation is already known
		unknown = [req for req in requestRes if not req in self.gCalEvents]
		if len(unknown) > 0:
			print "Discovered new reservations:"
			logging.info("Discovered new reservations:")
		for u in unknown:
			print u
			logging.info(str(u))
		self.gCalEvents = requestRes
		#create new schedule in case any changes occurred
		for req in self.gCalEvents:
			#schedule check 900 seconds after reservation start time
			eventtime = req.get_start_float(900)
			if not eventtime < time.time():
				#add event to scheduler and save event id
				self.runningSchedules.append( self.schedule.enterabs(eventtime, 1, self.machine_check, (req.host,)))
		print "Scheduled checks:"
		for item in self.schedule.queue:
			msg = "Check event at - " + time.strftime("%H:%M.%m/%d/%y", time.localtime(item.time))
			print msg
			logging.info(msg)

	def runRoutine(self, interval):
		while True:
			self.update()
			st = threading.Thread(target=self.schedule.run)
			st.start()
			time.sleep(interval)


#Start the main execution thread
def run(username, password, url, logfile, email, interval):
	checker = ReservationChecker(username, password, url, logfile, email)
	checker.runRoutine(interval)

if __name__ == "__main__":
	options, args = getopt.getopt(sys.argv[1:], "l", ["user=","pw=","url=","interval=","logfile=","email="])
	options = dict(options)
	if not "--url" in options:
		options["--url"] = raw_input("Enter url (include http:..) ")
	if not "--user" in options:
		options["--user"] = raw_input("Enter user name: ")
	if not "--pw" in options:
		options["--pw"] = getpass.getpass()
	logfile = "verifier.log"
	if "--logfile" in options:
		logfile = options["--logfile"]
	email = None
	if "--email" in options:
		#possible comma separated list of addresses
		email = [addr.lstrip().rstrip() for addr in options["--email"].split(",")]
		print email
	#the url for events needs to end in '/'
	if not options["--url"].endswith("/"):
		options["--url"] += "/"
	#interval is how often gCal should be checked for update
	interval = 3600
	if "--interval" in options:
		interval = int(options["--interval"])
		
	run(username=options["--user"], password=options["--pw"], url=options["--url"], logfile=logfile, email=email, interval=interval)
